package com.swak.boot.config.zookeeper.idgen;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.curator.framework.CuratorFramework;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import com.swak.App;
import com.swak.Constants;
import com.swak.OS;
import com.swak.boot.config.zookeeper.ZookeeperProperties;
import com.swak.incrementer.DistributeIdGenerator;
import com.swak.incrementer.Long64Generator;
import com.swak.registry.URL;
import com.swak.utils.Lists;
import com.swak.utils.Maps;
import com.swak.utils.Sets;
import com.swak.utils.StringUtils;
import com.swak.zookeeper.ZookeeperService;

/**
 * 开启 zk ServerSn 选择服务；
 * 
 * @author lifeng
 */
@ConditionalOnClass({ CuratorFramework.class, ZookeeperService.class })
@ConditionalOnProperty(prefix = Constants.APPLICATION_PREFIX, name = "enableZk", matchIfMissing = true)
@EnableConfigurationProperties(ZookeeperProperties.class)
public class DistributeIdAutoConfiguration {

	/**
	 * 创建分布式的ID生成器；
	 * 
	 * @return
	 * @throws Exception 
	 */
	@Bean
	public DistributeIdGenerator distributeIdGenerator(ZookeeperService zookeeperService, App app) throws Exception {
		String serverSn = this.createServerSn(zookeeperService, app);
		DistributeIdGenerator idGenerator = new DistributeIdGenerator(serverSn);
		app.setIdGenerator(idGenerator).setSn(serverSn);
		return idGenerator;
	}

	private String createServerSn(ZookeeperService zookeeperService, App app) throws Exception {
		URL url = this.createUrl(app);
		String servicePath = url.toServicePath();
		zookeeperService.create(servicePath, false);
		List<String> serverSns = zookeeperService.getChildren(servicePath);
		Set<String> serverSnsIndex = Lists.isEmpty(serverSns) ? Sets.newHashSet() : Sets.newHashSet(serverSns);
		long datacenterId = 1;
		long workerId = 0;

		// 尝试找到一个可用的序号
		while (true) {
			workerId++;
			if (workerId > Long64Generator.MAX_WORKER_ID) {
				workerId = 1;
				datacenterId++;
			}
			if (datacenterId > Long64Generator.MAX_DATACENTER_ID) {
				throw new com.swak.exception.ServerException(
						"分布式ID分配异常，已经超过数据中心允许的最大值" + Long64Generator.MAX_DATACENTER_ID);
			}
			String serverSn = StringUtils.format("server-%s-%s", datacenterId, workerId);
			if (serverSnsIndex.contains(serverSn)) {
				continue;
			}
			boolean flag = zookeeperService.applyfor(servicePath + "/" + serverSn, url.toFullString());
			if (!flag) {
				continue;
			}
			return serverSn;
		}
	}

	private URL createUrl(App app) throws Exception {
		Map<String, String> parameters = Maps.newHashMap();
		parameters.put(Constants.Application_KEY, app.getName());
		parameters.put(Constants.Service_Key, Constants.Registry_ServerSn_Sn_Key);
		parameters.put(Constants.Server_Key, app.getId());
		return new URL(Constants.Registry_ServerSn_Key, OS.ip(), 0, Constants.Registry_ServerSn_Sn_Key, parameters);
	}
}